<template>
  <div class="V-swiper">
    <div
      class="swiper"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    >
      <slot></slot>
    </div>
    <div class="indicator" v-if="showIndicator">
      <div
        :class="['indicator-item', { active: curIdx - 1 === index }]"
        v-for="(item, index) of slideLen"
        :key="index"
      ></div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Swiper',
  props: {
    showIndicator: { // 控制指示器的显示与隐藏
      type: Boolean,
      default: true
    },
    autoPlay: { // 控制是否自动轮播
      type: Boolean,
      default: true
    },
    speed: { // 每隔3000ms切换图片
      type: Number,
      default: 3000
    },
    duration: { // 过渡时间，默认500ms
      type: Number,
      default: 500
    },
    moveRatio: { // 默认手指移动时的距离超图片宽度0.25倍时切换图片
      type: Number,
      default: 0.25
    }
  },
  mounted () {
    this.handleDom()
    this.autoPlay && this.run()
  },
  data () {
    return {
      slideLen: 2, // 保存slide的个数
      swiperStyle: {}, // swiper的样式
      curIdx: 1, // 当前的索引
      totalWidth: 0, // swiper的宽度
      scrolling: false // 是否正在滚动
    }
  },
  methods: {
    /**
     * 操作DOM, 在DOM前后添加Slide
     */
    handleDom () {
      const swiperEl = document.querySelector('.swiper')
      const slideEls = document.getElementsByClassName('slide')

      // 将slideEls的个数保存在slideLen中
      this.slideLen = slideEls.length
      // 将swiperEl的样式保存在swiperStyle中
      this.swiperStyle = swiperEl.style
      // 将swiperEl的宽度保存在totalWidth中
      this.totalWidth = swiperEl.offsetWidth

      // 如果slideLen的值大于1，克隆第1个slide与最后1个slide并插入
      if (this.slideLen > 1) {
        const cloneFirst = slideEls[0].cloneNode(true)
        const cloneLast = slideEls[this.slideLen - 1].cloneNode(true)

        // 把克隆的最后1张插入为swiperEl的第1个子元素，把克隆的第1张插入为swiperEl的最后1个子元素
        swiperEl.insertBefore(cloneLast, slideEls[0])
        swiperEl.appendChild(cloneFirst)
      }

      // 让swiperEl, 显示第一个子元素(目前是显示前面添加的最后一个元素)
      this.setTransform(-this.curIdx * this.totalWidth)
    },
    /**
     * 设置滚动的位置
     */
    setTransform (position) {
      this.swiperStyle.transform = `translate3d(${position}px,0,0)`
    },
    /**
     * 自动轮播
     */
    run () {
      this.timer = setInterval(() => {
        this.curIdx++

        this.scrollContent(-this.curIdx * this.totalWidth)
      }, this.speed)
    },
    scrollContent (curPos) {
      // 设置正在滚动
      this.scrolling = true

      // 设置滚动时过渡效果
      this.swiperStyle.transition = `transform ${this.duration}ms`
      // 设置滚动距离
      this.setTransform(curPos)

      // 判断是否有滚动到正确位置
      this.checkPos()

      // 滚动完成
      this.scrolling = false
    },
    /**
       * 当curIdx发生变化时，判断是否有滚动到正确位置
       */
    checkPos () {
      if (this.curIdx >= this.slideLen + 1) {
        this.curIdx = 1

        // 当从最后1张滚动到第1张时取消过渡效果
        this.swiperStyle.transition = 'transform ' + 0 + 'ms'

        this.setTransform(-this.curIdx * this.totalWidth)
      } else if (this.curIdx <= 0) {
        this.curIdx = this.slideLen

        // 当从第1张滚动到最后1张时取消过渡效果
        this.swiperStyle.transition = 'transform ' + 0 + 'ms'

        this.setTransform(-this.curIdx * this.totalWidth)
      }
    },
    /**
     * 当手指触摸时获取初始位置
     */
    touchStart (e) {
      if (this.scrolling) return

      window.clearInterval(this.timer)

      // 手指触摸时获取初始位置并保存
      this.startX = e.touches[0].pageX
    },
    touchMove (e) {
      this.currentX = e.touches[0].pageX

      // 保存手指移动的距离
      this.moveX = this.currentX - this.startX

      // 图片移动的距离 = 图片原来的距离 + 手指移动的距离
      const imgMoveX = -this.curIdx * this.totalWidth + this.moveX

      this.setTransform(imgMoveX)
    },
    touchEnd () {
      const currentX = Math.abs(this.moveX)

      // 如果this.moveX < 0且当前移动的距离 > 图片宽度 * moveRatio，则切换图片，即右滑（下一张），反之this.moveX > 0且当前移动的距离 > 图片宽度 * moveRatio，则左滑（上一张）
      if (this.moveX === 0) {
        return
      } else if (this.moveX < 0 && currentX > this.totalWidth * this.moveRatio) {
        this.curIdx++
      } else if (this.moveX > 0 && currentX > this.totalWidth * this.moveRatio) {
        this.curIdx--
      }

      this.scrollContent(-this.curIdx * this.totalWidth)

      // 移动完成后重新自动轮播
      this.run()
    }
  }
}
</script>

<style lang="scss" scoped>
.V-swiper {
  position: relative;
  overflow: hidden;

  .swiper {
    display: flex;
  }

  .indicator {
    position: absolute;
    bottom: 8px;
    display: flex;
    width: 100%;
    justify-content: center;

    &-item {
      width: 8px;
      height: 8px;
      background-color: #fff;
      border-radius: 50%;
      box-sizing: border-box;
      margin: 0 5px;

      &.active {
        background-color: rgba(212, 62, 46, 1);
      }
    }
  }
}
</style>
